/*************************************************************************
	> File Name: job_v2.c
	> Author: Angel Criss
	> Mail: abbytsing@gmail.com 
	> Created Time: Sun, Jul 20, 2014  4:48:56 PM
 ************************************************************************/
// my system time is CST(UTC+08) with -8*3600 convert time to BIOS time(UTC)

#define _POSIX_C_SOURCE 199309
#include<time.h>
#include<string.h>		// strcmp()
#include<sys/time.h>
#include<sys/types.h>		// for gettid()
#include<pthread.h>
#include<signal.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>

pid_t pid;              // get it in main function

typedef struct job_file
{
  char *arg;			// timer file pass vlaue from argv[2]
  long relative_time;		// timer_time - current_time
  char *command;		// command in arg
} job;				// declare a new struct called job

void
err (char *ERR)
{
  perror (ERR);
  exit (errno);
}

void
Usage (char *arg0)
{
  printf ("%s %s %s\nor\n%s %s %s\n", arg0, "-f", "path", arg0, "hh:mm:ss",
	  "command");
  printf ("or\ndisable timer by using: %s -d\n", arg0);
  printf ("or\ndelete all timers by using: %s -a\n", arg0);
  exit (EXIT_SUCCESS);
}

long
get_curr_time ()
{
  struct timeval time_value;
  struct tm *curr;
  long curr_ti;			// current time

  /* return current time in a struct */
  if (gettimeofday (&time_value, NULL) == -1)
    err ("gettimeofday");
  else
    {
      /* convert to another struct in format hour:min:sec */
      if ((curr = gmtime (&time_value.tv_sec)) == NULL)
	err ("gmtime");
      else
	curr_ti = (long) curr->tm_hour * 3600 + (long) curr->tm_min * 60
	  + (long) curr->tm_sec;
    }

  return curr_ti;
}

void
exec (union sigval val)
{
  system (val.sival_ptr);	// exec the command

}

job
job_timer (job j_arg)
{
  struct sigevent sevp;		// use to modify notify mode
  timer_t timerid;
  struct itimerspec value;	// use to start and stop timer

  sevp.sigev_notify = SIGEV_THREAD;	// notify by using pthread
  sevp.sigev_notify_function = exec;	// declare execute command fucntion
  sevp.sigev_notify_attributes = NULL;	// set the attr to NULL
  sevp.sigev_value.sival_ptr = j_arg.command;	// pass the command to pthread

  value.it_interval.tv_sec = 0;
  value.it_interval.tv_nsec = 0;	// set onshot timer
  value.it_value.tv_sec = j_arg.relative_time;	// set timer expiration time
  value.it_value.tv_nsec = 0;

  if (timer_create (CLOCK_REALTIME, &sevp, &timerid) == -1)
    err ("timer_create");

  if (timer_settime (timerid, 0, &value, NULL) == -1)
    err ("timer_settime");
  return j_arg; //if no such a line,with -Wall will report 'reaches the end of non-void function'
}

void
pid_record (char *para)
{
  FILE *fp;
  int fd;
  int i = 0;
  int j = 0;
  int k = 0;
  struct stat buf;
  off_t size;
  char list[10];                // set pid lemgth limit
  char tab[100][10];           // pid tab
  char *pid_list;

  if (strcmp (para, "create") == 0)
  {
      fp = fopen ("jobid", "a+");

      fprintf (fp, "%d", pid+1);
      fprintf (fp, "%c", '\n');

      fclose (fp);
  }
  else if (strcmp (para, "delete") == 0)
  {
      stat ("jobid", &buf);       // if compile with -Wall will report warning when use lstat
      size = buf.st_size;

      pid_list = malloc (size);

      if ((fd = open ("jobid", O_RDWR)) == -1)
	    err ("open_jobid");

      if (read (fd, pid_list, size) == -1)
	    err ("read");

      while (pid_list[i] != '\n')
	  {
        list[i] = pid_list[i];
	    i++;
	  }

      close (fd);

      free (pid_list);

      //remove ("jobid");

      kill (atoi (list), 15);  // +1 is the real working process id

      printf ("timer has been deleted\n"); // will never execute

      system("sed -i '1d' jobid");          // a temporary way to delete timers one by one

      exit (EXIT_SUCCESS); // will never execute
  }
  else if (strcmp (para, "crash")==0)
  {
      stat ("jobid", &buf);       // if compile with -Wall will report warning when use lstat
      size = buf.st_size;

      pid_list = malloc (size);

      if ((fd = open ("jobid", O_RDWR)) == -1)
	    err ("open_jobid");

      if (read (fd, pid_list, size) == -1)
	    err ("read");

      close (fd);

      while(pid_list[i]!='\0')
      {
          if(pid_list[i]=='\n')
          {
              j++;
              k=0;
          }
          tab[j][k]=pid_list[i];
          k++;
          i++;
      }

      i=j;

      for(j=0;j<i;j++)
      {
          printf ("now deleting timer: %d\n",atoi(tab[j]));
          sleep(1);
          kill(atoi(tab[j]),15);
      }

      remove("jobid");
      printf("all timers have been deleted\n");
      exit(EXIT_SUCCESS);
  }
}

job
_file (job j_file)
{
  int job_file;			// associate to the job_file in main()
  char *buf;			// assume that a commad length range from 0 to 100
  struct stat filestat;		// using to get file size ( if not using while(read()) read)
  int size;			// file szie
  int i = 9, j = 0;		// for reading the command (command start from 9bit)
  long hour_high, minute_high, second_high, timer_seconds;
  long curr_time;		// get the return of 'get_curr_time()'


  if ((job_file = open (j_file.arg, O_RDONLY)) == -1)
    err ("job_file open");

  fstat (job_file, &filestat);
  size = filestat.st_size;

  /* need to free the malloc space */

  buf = malloc (size);
  j_file.command = malloc (size);

  if (read (job_file, buf, size) == -1)
    err ("read job_file");

  hour_high = atol (&buf[0]);	// 为什么 buf[0] 的内容竟然是 本该 buf[0] buf[1]的内容?
  minute_high = atol (&buf[3]);
  second_high = atol (&buf[6]);

  timer_seconds = (hour_high) * 3600
    + (minute_high) * 60 + second_high - 8 * 3600;
  // convert time format to second; 8*3600 convert to BIOS time 

  curr_time = get_curr_time ();	// invok a function

/* if timer time is little than curr_time then 
 * the timer will be seted is the next day's timer
 */
  j_file.relative_time =
    (timer_seconds <
     curr_time) ? (timer_seconds + 24 * 3600) - curr_time : timer_seconds -
    curr_time;

  while (buf[i+1] != '\0')
    {
      j_file.command[j] = buf[i];
      i++;
      j++;
    }
  
  printf("cmd is : %s\n",j_file.command);
  free(buf);
  close(job_file);

  return j_file;
}

job
_para (job j_para)
{
  long hour, min, sec, timer_time;
  long curr_time;

  /* why use arg[0] arg[3] arg[6] see comment in _file() */
  hour = atol (&j_para.arg[0]);
  min = atol (&j_para.arg[3]);
  sec = atol (&j_para.arg[6]);

  /* -8*3600 convert to BIOS time */
  timer_time = hour * 3600 + min * 60 + sec - 8*3600;

  curr_time = get_curr_time ();

  j_para.relative_time =
    (timer_time <
     curr_time) ? (timer_time + 24 * 3600) - curr_time : timer_time -
    curr_time;
  return j_para;
}

int
main (int argc, char *argv[])
{
  /* declare the argument of invok function and use this to take the return value */
  job j_file, j_para;
  pid=getpid();

  if (argc < 3 || strcmp (argv[1], "--help") == 0)
    {
        if(argc>1)
        {
            if (strcmp (argv[1], "-d") == 0)
                pid_record ("delete");
            if (strcmp (argv[1],"-a")==0)
                pid_record ("crash");
        }
       Usage (argv[0]);
    }

  pid_record ("create");

  if (strcmp (argv[1], "-f") == 0)
    {

      switch (fork ())
	{
	case -1:
	  err ("son_fork");
	case 0:
	  j_file.arg = argv[2];
	  j_file = _file (j_file);
	  printf ("the relative (main) is: %ld \n", j_file.relative_time);
	  printf ("the command is: %s \n", j_file.command);
	  job_timer (j_file);
	  pause ();		// wait a signal from thread
	default:
	  break;		// make son be reaped by father
	}
      exit (0);
    }
  else
    {

      switch (fork ())
	{
	case -1:
	  err ("son_fork");
	case 0:
	  j_para.arg = argv[1];
	  j_para.command = argv[2];
	  j_para = _para (j_para);
	  printf ("the relative (main) is: %ld \n", j_para.relative_time);
	  printf ("the command is: %s \n", j_para.command);
	  job_timer (j_para);
	  pause ();		// wait a signal from thread
	default:
	  break;		// make son be reaped by father
	}
    }

  exit (EXIT_SUCCESS);  // will never execute
}
